﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Snowdreamist.ComponentModel
{
    public class ThreadSafeList<T> : IList<T>
    {
        public ThreadSafeList()
            : this(new List<T>())
        {
        }

        public ThreadSafeList(IEnumerable<T> collection)
            : this(new List<T>(collection))
        {
        }

        public ThreadSafeList(int capacity)
            : this(new List<T>(capacity))
        {
        }

        public ThreadSafeList(IList<T> list)
        {
            this._list = list;
            this._lock = new ReaderWriterLockSlim();
        }

        public int IndexOf(T item)
        {
            this._lock.EnterReadLock();

            try
            {
                var r = this._list.IndexOf(item);
                this._lock.ExitReadLock();
                return r;
            }
            catch
            {
                this._lock.ExitReadLock();
                throw;
            }
        }

        public void Insert(int index, T item)
        {
            this._lock.EnterWriteLock();

            try
            {
                this._list.Insert(index, item);
            }
            catch
            {
                this._lock.ExitWriteLock();
                throw;
            }

            this._lock.ExitWriteLock();
        }

        public void RemoveAt(int index)
        {
            this._lock.EnterWriteLock();

            try
            {
                this._list.RemoveAt(index);
            }
            catch
            {
                this._lock.ExitWriteLock();
                throw;
            }

            this._lock.ExitWriteLock();
        }

        public T this[int index]
        {
            get
            {
                this._lock.EnterReadLock();

                try
                {
                    var r = this._list[index];
                    this._lock.ExitReadLock();
                    return r;
                }
                catch
                {
                    this._lock.ExitReadLock();
                    throw;
                }
            }
            set
            {
                this._lock.EnterWriteLock();
                try
                {
                    this._list[index] = value;
                }
                catch
                {
                    this._lock.ExitReadLock();
                    throw;
                }
                this._lock.ExitWriteLock();
            }
        }

        public void Add(T item)
        {
            this._lock.EnterWriteLock();

            try
            {
                this._list.Add(item);
            }
            catch
            {
                this._lock.ExitWriteLock();
                throw;
            }

            this._lock.ExitWriteLock();
        }

        public void Clear()
        {
            this._lock.EnterWriteLock();

            try
            {
                this._list.Clear();
            }
            catch
            {
                this._lock.ExitWriteLock();
                throw;
            }

            this._lock.ExitWriteLock();
        }

        public bool Contains(T item)
        {
            this._lock.EnterReadLock();

            try
            {
                var r = this._list.Contains(item);
                this._lock.ExitReadLock();
                return r;
            }
            catch
            {
                this._lock.ExitReadLock();
                throw;
            }
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            this._lock.EnterReadLock();

            try
            {
                this._list.CopyTo(array, arrayIndex);
            }
            catch
            {
                this._lock.ExitReadLock();
                throw;
            }

            this._lock.ExitReadLock();
        }

        public int Count
        {
            get
            {
                this._lock.EnterReadLock();

                try
                {
                    var r = this._list.Count;
                    this._lock.ExitReadLock();
                    return r;
                }
                catch
                {
                    this._lock.ExitReadLock();
                    throw;
                }
            }
        }

        public bool IsReadOnly
        {
            get
            {
                return this._list.IsReadOnly;
            }
        }

        public bool Remove(T item)
        {
            this._lock.EnterWriteLock();

            try
            {
                var r = this._list.Remove(item);
                this._lock.ExitWriteLock();
                return r;
            }
            catch
            {
                this._lock.ExitWriteLock();
                throw;
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            return this._list.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return (this._list as System.Collections.IEnumerable).GetEnumerator();
        }

        public ThreadCriticalRegion EnumerableRegion 
        {
            get
            {
                return new ThreadSafeListEnumerableRegion(this._lock);
            }
        }

        #region private

        private IList<T> _list;

        private ReaderWriterLockSlim _lock;

        private class ThreadSafeListEnumerableRegion : ThreadCriticalRegion
        {
            public ThreadSafeListEnumerableRegion(ReaderWriterLockSlim rwLock)
            {
                this._lock = rwLock;
            }

            public override void Enter()
            {
                this._lock.EnterReadLock();
            }

            public override void Exit()
            {
                this._lock.ExitReadLock();
            }

            #region private

            private ReaderWriterLockSlim _lock;

            #endregion
        }

        #endregion
    }
}
